﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Threading;
using System.DirectoryServices;
using System.Collections;

using groupAnalyzer.Observers;
using groupAnalyzer.model;
using groupAnalyzer;
using groupAnalyzer.connectivity;

namespace groupAnalyzer.controller
{
    class Controller : ObserverClient
    {
        // maybe changed to queue for mass work
        private volatile Messages message;
        private volatile Thread myThread;
        private Factory factory;
        private object payLoad;

        private string lastObjectDN;
        private string lastAnalyzeMode;

        private LdapConnectivity connectivity;
        Dictionary<string, DirectoryEntry> rawGroups;

        public void notify(Messages message, object payLoad)
        {
            this.message = message;
            this.payLoad = payLoad;
            // wake worker
            if (this.myThread.ThreadState == ThreadState.Suspended)
                this.myThread.Resume();
        }

        private void analyzeSingleGroup(string objectDN, string analyzeMode, LdapConnectivity connectivity)
        {
            NodeAnalyzer analyzer = new NodeAnalyzer(this.factory, analyzeMode, this.rawGroups,connectivity);
            this.analyzeState(objectDN, analyzeMode, analyzer);
            Observer.getGlobalObserver().notifiy(Messages.analyzed, null);
        }

        private void analyzeState(string objectDN, string analyzeMode, NodeAnalyzer analyzer)
        {
            // clear out all objects

            // saving parameters
            this.lastObjectDN = objectDN;
            this.lastAnalyzeMode = analyzeMode;
            analyzer.analyze(objectDN);
        }

        private void loadGroups()
        {
            if (this.rawGroups == null)
            {
                this.connectivity = LdapConnectivity.constructLdapConnectivity();
                this.rawGroups = connectivity.getAllGroupsHashmap();
            }
            Console.WriteLine("finished loading groups");
        }

        public void run()
        {
            // load config
            try
            {
                Settings.loadSettings();
            }
            catch (Exception e)
            {
                Observer.getGlobalObserver().notifiy(Messages.error, e.Message);
            }
            this.loadGroups();
            NodeAnalyzer analyzer = new NodeAnalyzer(this.factory, rawGroups, connectivity);
            
            while (true)
            {
                if (this.message == Messages.nothing)
                {
                    // be nice to cpu
                    this.myThread.Suspend();
                }

                // preblock
                switch (this.message)
                {
                    case Messages.analyzeAll:
                    case Messages.analyze:

                        break;
                }
                switch (this.message)
                {
                    case Messages.loadSettings:
                        Settings.loadSettings();
                        break;

                    case Messages.refresh:
                        this.factory.reset();
                        if (this.lastObjectDN == null)
                        {
                            // maybe change to do nothing...
                            Observer.getGlobalObserver().notifiy(Messages.error, "Nothing to refresh");
                            break;
                        }
                        this.analyzeSingleGroup(this.lastObjectDN, this.lastAnalyzeMode, connectivity);
                        break;

                    case Messages.analyzeAll:
                        {
                            this.factory.reset();
                            string analyzeModeString = (string)this.payLoad;
                            if (!analyzeModeString.Equals("member"))
                            {
                                if (!analyzeModeString.Equals("memberof"))
                                {
                                    Observer.getGlobalObserver().notifiy(Messages.error, "No valid analyzer mode given");
                                    break;
                                }
                            }
                            analyzer.RawGroups = this.rawGroups;
                            analyzer.AnalyzeMode = analyzeModeString;
                            for (int i = 0; i < analyzer.RawGroups.Count; i++)
                            {
                                DirectoryEntry entry = analyzer.RawGroups.ToList()[i].Value;

                                string fqdn = connectivity.getProperty(entry, "distinguishedName", 0);
                                this.analyzeState(fqdn, analyzeModeString, analyzer);
                            }
                            Observer.getGlobalObserver().notifiy(Messages.analyzed, null);
                            break;
                        }

                    case Messages.analyze:
                        {
                            this.factory.reset();
                            if (this.payLoad == null)
                            {
                                Observer.getGlobalObserver().notifiy(Messages.error, "No object dn given");
                                break;
                            }
                            List<object> payloadArray = ((List<object>)this.payLoad);
                            string objectDN = (string)payloadArray[0];
                            string analyzeModeString = (string)payloadArray[1];

                            analyzer.RawGroups = this.rawGroups;
                            if (!analyzeModeString.Equals("member"))
                            {
                                if (!analyzeModeString.Equals("memberof"))
                                {
                                    Observer.getGlobalObserver().notifiy(Messages.error, "No valid analyzer mode given");
                                    break;
                                }
                            }

                            this.analyzeSingleGroup(objectDN, analyzeModeString, connectivity);
                            break;
                        }

                    case Messages.exit:
                        Environment.Exit(0);
                        break;

                    default:
                        break;
                }

                // post-block 
                switch (this.message)
                {
                    case Messages.analyzed:
                        this.loadGroups();
                        break;
                }

                // nothing to do any more - set it to nothing
                this.message = Messages.nothing;
            }
        }

        public Controller(Factory factory)
        {
            this.factory = factory;
            this.message = Messages.nothing;


            this.myThread = new Thread(new ThreadStart(this.run));
            myThread.Start();
        }

        public static void init(Factory factory)
        {
            Controller controller = new Controller(factory);
            Observer.getGlobalObserver().register(controller);
        }
    }
}
